home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1987 / 03 / dunc.lst next >
File List  |  1987-02-06  |  18KB  |  661 lines

  1.  *  paste -- a program to attach to the lines of a file the correspond-    *
  2.  *    ing lines of another file, with an optional string between    *
  3.  *    them.                        *
  4.  *                            *
  5.  *  Written January, 1984 by John M. Gamble            *
  6.  *  Updated for UNIX April, 1986                *
  7.  *                            *
  8.  *  usage:                            *    
  9.  *                            *
  10.  *  paste [-paste] [-b <string>] [-<n>] [file1] [file2]        *
  11.  *                            *
  12.  *  options:                        *
  13.  *                            *
  14.  *  -p    <file1> does not exist (<string> is prepended to each    *
  15.  *    line.)                        *
  16.  *                            *
  17.  *  -a    <file2> does not exist (<string> is appended to each    *
  18.  *    line.)                        *
  19.  *                            *
  20.  *  -s    Do not print <string> with lines from only one file.    *
  21.  *                            *
  22.  *  -t    An option to resolve the ambiguous command        *
  23.  *    "paste <file>".     The -t flag forces <file> to trail    *
  24.  *    standard input.  I.e.,                *
  25.  *                            *
  26.  *    "paste <file>"                    *
  27.  *        is equivalent to "paste <file> <stdin>"        *
  28.  *                            *
  29.  *    "paste -t <file>"                    *
  30.  *        is equivalent to "paste <stdin> <file>".        *
  31.  *                            *
  32.  *  -e    Do not print <string> if both input lines are empty        *
  33.  *    (i.e., that consist of no characters but '\n'.)        *
  34.  *                            *
  35.  *  -b <string> A string of characters to be inserted between the lines    *
  36.  *          of <file1> and <file2>.  The string may contain all    *
  37.  *          the standard escape codes with the exception of '\0'.    *
  38.  *          The string may also indicate blanks with the escape    *
  39.  *          sequence '\s'.                    *
  40.  *                            *
  41.  *  -<n>    Print <n> lines of <file1> before appending lines of    *
  42.  *            <file2>.  If <n> is negative (e.g., "paste --3") then    *
  43.  *        <n> lines of <file2> will be printed first.    *
  44.  *----------------------------------------------------------------------*/
  45.  
  46. #include    <stdio.h>
  47. #include    <ctype.h>
  48.  
  49. /* On systems such as UNIX, if a string with blanks in it is
  50.  * surrounded by quote marks, it is considered to be one string.
  51.  * On other systems, the blank ends the string and the quote
  52.  * marks are passed along with the other characters.  So, while
  53.  * on UNIX, the command
  54.  *
  55.  *     'paste -b "; do " list1 list2'
  56.  *
  57.  * would set 
  58.  *    argv[2] to    "; do ",
  59.  *    argv[3] to    list1,
  60.  *    argv[3] to    list2.
  61.  *
  62.  * a system like MSDOS would set 
  63.  *    argv[2] to    "\";",
  64.  *    argv[3] to    "do",
  65.  *    argv[4] to    "\"",
  66.  *    argv[5] to    list1,
  67.  *    argv[6] to    list2.
  68.  *
  69.  * This is easily taken care of, but it does mean that conditional
  70.  * compilation is required by setting the switch below to either
  71.  * zero or one, depending on your particular operating system.
  72.  */
  73. #define BLANK_ENDS_STR        0
  74.  
  75. #define STRINGLEN        128
  76. #define TRUE            1
  77. #define FALSE            0
  78.  
  79. #define isoctal(x)        ((x) >= '0' && (x) < '8')
  80.  
  81. typedef unsigned int        Boolean;
  82.  
  83. char    bstring[STRINGLEN] = {'\0'};
  84.  
  85. char    *nullstr = "";
  86. char    *strf = "%s";
  87. char    *program_name = "paste";
  88. char    *error_msg[] =
  89.         {
  90. /*0*/    "usage: %s [-aptse] [-b \"string\"] [-<n>] [file1] [file2] %s\n",
  91. /*1*/    "%s: unknown flag %s\n",
  92. /*2*/    "%s: at least one file must exist%s\n",
  93. /*3*/    "%s: -t flag is only valid with one file on the command line%s\n",
  94. /*4*/    "%s: both files can't be standard input%s\n",
  95. /*5*/    "%s: contradictory options%s\n",
  96. /*6*/    "%s: can't open %s\n",
  97. /*7*/    "%s: -a or -p flags are invalid with two files%s\n",
  98. /*8*/    "%s: too many files%s\n",
  99. /*9*/    "%s: string argument lacks closing \' or \"%s\n",
  100. /*10*/    "%s: null character in string argument%s\n",
  101. /*11*/    "%s: string argument too long%s\n"
  102.     };
  103.  
  104. main(argc, argv)
  105. int        argc;
  106. char        **argv;
  107. {
  108.    FILE        *fp1, *fp2, *fopen();
  109.    Boolean    prepend = FALSE, append = FALSE,  trailing = FALSE;
  110.    Boolean    printempty = TRUE, printsingle = TRUE;
  111.    int        slip = 0;
  112.    char        *subarg;
  113.  
  114.  
  115.    if (argc == 1)
  116.       exit_error(0, nullstr);
  117.  
  118.    /* Get the flags.
  119.     */
  120.    while (--argc > 0 && **++argv == '-')
  121.    {
  122.       switch (*(*argv + 1))
  123.       {
  124.          case '\0':        /* Because default: won't catch this.*/
  125.             exit_error(1, *argv);
  126.             break;
  127.  
  128.          case 'b':
  129.             if (argc == 1)
  130.                exit_error(0, nullstr);
  131.  
  132.             argc--;
  133.             argv++;
  134.  
  135. #if BLANK_ENDS_STR
  136.             strget(&argc, &argv, bstring);
  137. #else
  138.             strload(*argv, bstring);
  139. #endif
  140.             break;
  141.  
  142.          case '-':
  143.          case '0':
  144.          case '1':
  145.          case '2':
  146.          case '3':
  147.          case '4':
  148.          case '5':
  149.          case '6':
  150.          case '7':
  151.          case '8':
  152.          case '9':
  153.             slip = atoi(*argv + 1);
  154.             break;
  155.  
  156.          default:
  157.             subarg = *argv;
  158.             while (*++subarg)
  159.             {
  160.                switch (*subarg)
  161.                {
  162.                   case 'a':
  163.                      append = TRUE;
  164.                      break;
  165.  
  166.                   case 'e':
  167.                      printempty = FALSE;
  168.                      break;
  169.  
  170.                   case 'p':
  171.                      prepend = TRUE;
  172.                      break;
  173.  
  174.                   case 's':
  175.                      printsingle = FALSE;
  176.                      break;
  177.  
  178.                   case 't':
  179.                      trailing = TRUE;
  180.                      break;
  181.  
  182.                   default:
  183.                      exit_error(1, *argv);
  184.                      break;
  185.             }
  186.             break;
  187.          }
  188.       }
  189.    }
  190.  
  191.    if (prepend && append)    /* Contradictory options.*/
  192.       exit_error(2, nullstr);
  193.  
  194.    switch (argc)    /* The number of file names on the command line.*/
  195.    {
  196.       case 0:
  197.          if (trailing)
  198.             exit_error(3, nullstr);
  199.  
  200.          if (!(prepend || append))    /* Both files can't be stdin.*/
  201.             exit_error(4, nullstr);
  202.  
  203.          if (append)
  204.             attachf(stdin, NULL, slip, printsingle, printempty);
  205.  
  206.          else
  207.             attachf(NULL, stdin, slip, printsingle, printempty);
  208.          break;
  209.  
  210.       case 1:
  211.  
  212.          /* Contradictory options?
  213.           */
  214.          if (trailing && (prepend || append))
  215.             exit_error(5, nullstr);
  216.  
  217.          if ((fp1 = fopen(*argv, "r")) == NULL)
  218.             exit_error(6, *argv);
  219.  
  220.          if (append)
  221.             attachf(fp1, NULL, slip, printsingle, printempty);
  222.  
  223.          else if (prepend)
  224.             attachf(NULL, fp1, slip, printsingle, printempty);
  225.  
  226.          else if (trailing)
  227.             attachf(stdin, fp1, slip, printsingle, printempty);
  228.  
  229.          else
  230.             attachf(fp1, stdin, slip, printsingle, printempty);
  231.  
  232.          fclose(fp1);
  233.          break;
  234.  
  235.       case 2:
  236.          if (trailing)
  237.             exit_error(3, nullstr);
  238.  
  239.          if (prepend || append)
  240.             exit_error(7, nullstr);
  241.  
  242.          if ((fp1 = fopen(*argv, "r")) == NULL)
  243.             exit_error(6, *argv);
  244.  
  245.          if ((fp2 = fopen(*++argv, "r")) == NULL)
  246.             exit_error(6, *argv);
  247.  
  248.          attachf(fp1, fp2, slip, printsingle, printempty);
  249.          fclose(fp1);
  250.          fclose(fp2);
  251.          break;
  252.  
  253.       default:
  254.          exit_error(8, nullstr);
  255.          break;
  256.    }
  257.  
  258.    exit(0);
  259. }                    /* End of main.*/
  260.  
  261. #if BLANK_ENDS_STR
  262. /*----------------------------------------------------------------------*
  263.  * strget -- retrieve the <string> argument from the command line.    *
  264.  *      If the string contains blanks, C assumes this is the end of the    *
  265.  *      string, and places a \0 at its end.  Since WE know that it's    *
  266.  *      just a blank, we put one in, update the position of argv, and    *
  267.  *      decrement argc.  Escape sequences are treated just as defined    *
  268.  *      in C (except \0, which is an error).  One extra escape sequence    *
  269.  *      ('\s') exists to handle multiple blanks on a line, for even if    *
  270.  *      the string is enclosed in quotes the extra blanks will not be    *
  271.  *      passed from the command line.                *
  272.  *----------------------------------------------------------------------*/
  273. strget(pargc, pargv, bstr)
  274. int        *pargc;
  275. char        ***pargv;
  276. char        *bstr;
  277. {
  278.    register int    j;
  279.    char        *subarg;
  280.    Boolean    st_quote = FALSE;
  281.    Boolean    st_apost = FALSE;
  282.  
  283.    subarg = **pargv;
  284.  
  285.    /* If the string is begun with a quote or an apostrophe, remember
  286.     * so that we know when to end the string.
  287.     */
  288.    if ((st_quote = (*subarg == '"')) || (st_apost = (*subarg == '\'')))
  289.       subarg++;
  290.  
  291.    for (j = 0; j < STRINGLEN; bstr++, subarg++, j++)
  292.    {
  293.       /* A `"` or `'` encountered could mean the end of a string -
  294.        * check against st_quote or st_apost.
  295.        */
  296.       if ((st_quote && *subarg == '"') ||
  297.           (st_apost && *subarg == '\''))
  298.          break;
  299.  
  300.       else if (*subarg == '\0')        /* Blank encountered in string.*/
  301.       {
  302.  
  303.          /* If we began with a quote, we are not finished.
  304.           */
  305.          if (st_quote || st_apost)
  306.          {
  307.             /* If nothing is left on the command line,
  308.              * a quote mark is missing.
  309.              */
  310.             if (--(*pargc) == 0)
  311.                exit_error(9, nullstr);
  312.  
  313.             /* Put the blank in, and point subarg
  314.              * to the next argv string.
  315.              */
  316.             *bstr = ' ';
  317.             subarg = *(++(*pargv)) - 1;
  318.          }
  319.  
  320.          /* Otherwise we didn't start with a quote mark - end.
  321.           */
  322.          else
  323.             break;
  324.       }
  325.  
  326.       else if (*subarg == '\\')        /* Escape sequences.*/
  327.          switch(*++subarg)
  328.          {
  329.             /* Nothing after the '\' - let the 'blank'
  330.              * section handle it.
  331.              */
  332.             case '\0':
  333.                bstr--;
  334.                subarg--;
  335.                j--;
  336.                break;
  337.  
  338.             case '"':
  339.                *bstr = '"';
  340.                break;
  341.  
  342.             case '0':
  343.             case '1':
  344.             case '2':
  345.             case '3':
  346.             case '4':
  347.             case '5':
  348.             case '6':
  349.             case '7':
  350.                *bstr = (char) bit_pattern(&subarg);
  351.                break;
  352.  
  353.             case '\\':
  354.                *bstr = '\\';
  355.                break;
  356.  
  357.             case 'b':
  358.                *bstr = '\b';
  359.                break;
  360.  
  361.             case 'f':
  362.                *bstr = '\f';
  363.                break;
  364.  
  365.             case 'n':
  366.                *bstr = '\n';
  367.                break;
  368.  
  369.             case 'r':
  370.                *bstr = '\r';
  371.                break;
  372.  
  373.             case 't':
  374.                *bstr = '\t';
  375.                break;
  376.  
  377.             case 's':
  378.                *bstr = ' ';
  379.                break;
  380.  
  381.             default:
  382.                *bstr = *subarg;
  383.                break;
  384.          }
  385.  
  386.       else
  387.          *bstr = *subarg;    /* No special character handling.*/
  388.    }
  389.  
  390.    if (j == STRINGLEN)
  391.       exit_error(11, nullstr);
  392.  
  393.    *bstr = '\0';
  394. }                /* End of strget.*/
  395. #else
  396.  
  397. /*----------------------------------------------------------------------*
  398.  * strload -- retrieve the <string> argument from the command line.    *
  399.  *      Escape sequences are treated just as defined in C (except \0,    *
  400.  *      which is an error).  One extra escape sequence ('\s') exists in    *
  401.  *      order to handle multiple blanks on a line without bothering to    *
  402.  *      enclose the string in quotes.                *
  403.  *----------------------------------------------------------------------*/
  404. strload(subarg, bstr)
  405. char        *subarg;
  406. char        *bstr;
  407. {
  408.    extern char    *nullstr;
  409.    register int    j;
  410.  
  411.  
  412.    for (j = 0; *subarg && j < STRINGLEN; bstr++, subarg++, j++)
  413.  
  414.       if (*subarg == '\\')    /* Escape sequences.*/
  415.          switch(*++subarg)
  416.             {
  417.             case '0':
  418.             case '1':
  419.             case '2':
  420.             case '3':
  421.             case '4':
  422.             case '5':
  423.             case '6':
  424.             case '7':
  425.                *bstr = (char) bit_pattern(&subarg);
  426.                break;
  427.  
  428.             case '\\':
  429.                *bstr = '\\';
  430.                break;
  431.  
  432.             case 'b':
  433.                *bstr = '\b';
  434.                break;
  435.  
  436.             case 'f':
  437.                *bstr = '\f';
  438.                break;
  439.  
  440.             case 'n':
  441.                *bstr = '\n';
  442.                break;
  443.  
  444.             case 'r':
  445.                *bstr = '\r';
  446.                break;
  447.  
  448.             case 't':
  449.                *bstr = '\t';
  450.                break;
  451.  
  452.             case 's':
  453.                *bstr = ' ';
  454.                break;
  455.  
  456.             default:
  457.                *bstr = *subarg;
  458.                break;
  459.             }
  460.  
  461.       else
  462.          *bstr = *subarg;    /* No special character handling.*/
  463.  
  464.  
  465.    if (j == STRINGLEN)
  466.       exit_error(11, nullstr);
  467.  
  468.    *bstr = '\0';
  469. }                /* End of strload.*/
  470. #endif
  471.  
  472. /*----------------------------------------------------------------------*
  473.  * bit_pattern -- Change the \ddd format to a character symbol. It will    *
  474.  *    check to see if there are (at most two) other octal digits     *
  475.  *    present.  It does not allow the return of the null character.    *
  476.  *    The pointer *ddd is only incremented by one for each extra    *
  477.  *    digit, because the pointer will be incremented again upon    *
  478.  *    returning from the function.                *
  479.  *----------------------------------------------------------------------*/
  480. bit_pattern(ddd)
  481. char        **ddd;
  482. {
  483.    extern char    *nullstr;
  484.    int        num;
  485.  
  486.    num = **ddd - '0';    /* Num is octal, otherwise we wouldn't be here.*/
  487.  
  488.    if (isoctal(*(*ddd + 1)))    /* Is the next character an octal digit?*/
  489.    {
  490.       num = 8 * num + *++*ddd - '0';
  491.  
  492.       if (isoctal(*(*ddd +1)))        /* How about this character?*/
  493.          num = 8 * num + *++*ddd - '0';
  494.    }
  495.  
  496.    if (!num)
  497.       exit_error(10, nullstr);        /* No \0 allowed.*/
  498.  
  499.    return(num);
  500.  
  501. }                /* End of bit_pattern.*/
  502.  
  503. /*----------------------------------------------------------------------*
  504.  * attachf -- Take the lines of <file2>, if any, and attach them    *
  505.  *       to the lines of <file1>, if any.  Slip determines how many    *
  506.  *       lines of <file1> (<file2> if negative) are printed before     *
  507.  *       printing the lines from both files together. It is possible to    *
  508.  *       specify some slippage even if the -a or -p flags are present.    *
  509.  *       This is not an error.  Attachf is smart enough to skip slip    *
  510.  *       in that case.                    *
  511.  *----------------------------------------------------------------------*/
  512. attachf(fp1, fp2, slip, printsingle, printempty)
  513. FILE        *fp1, *fp2;
  514. int        slip;
  515. Boolean        printsingle, printempty;
  516. {
  517.    Boolean    notempty;
  518.    register int    nxtc;
  519.  
  520.    /* Handle slippage, if any, up to the end of the file.
  521.     */
  522.    for (; slip > 0 && fp1 != NULL; slip--)
  523.    {
  524.       notempty = (nxtc = nextc(fp1)) != '\n';
  525.  
  526.       if (nxtc == EOF)
  527.       {
  528.          fp1 = NULL;
  529.          break;
  530.       }
  531.  
  532.       put_line(fp1);
  533.  
  534.       if (printsingle && (printempty || notempty))
  535.          printf(strf, bstring);
  536.  
  537.       putchar('\n');
  538.    }
  539.  
  540.    if (slip < 0)
  541.       slip = -slip;
  542.  
  543.    for (; slip > 0 && fp2 != NULL; slip--)
  544.    {
  545.       if ((nxtc = nextc(fp2)) == EOF)
  546.       {
  547.          fp2 = NULL;
  548.          break;
  549.       }
  550.  
  551.       if (printsingle && (printempty || nxtc != '\n'))
  552.          printf(strf, bstring);
  553.  
  554.       put_line(fp2);
  555.       putchar('\n');
  556.    }
  557.  
  558.    /* Paste the lines of each file together.
  559.     */
  560.    while (fp1 != NULL && fp2 != NULL)
  561.    {
  562.       notempty = (nxtc = nextc(fp1)) != '\n';
  563.  
  564.       if (nxtc == EOF)
  565.       {
  566.          fp1 = NULL;
  567.          break;
  568.       }
  569.  
  570.       put_line(fp1);
  571.  
  572.       if (printempty || notempty || nextc(fp2) != '\n')
  573.          printf(strf, bstring);
  574.  
  575.       put_line(fp2);
  576.  
  577.       if (nextc(fp2) == EOF)
  578.       {
  579.          fp2 = NULL;
  580.       }
  581.  
  582.       putchar('\n');
  583.    }
  584.  
  585.    while (fp1 != NULL)
  586.    {
  587.       notempty = (nxtc = nextc(fp1)) != '\n';
  588.       put_line(fp1);
  589.  
  590.       if (nextc(fp1) == EOF)
  591.          fp1 = NULL;
  592.  
  593.       if (printsingle && (printempty || notempty ))
  594.          printf(strf, bstring);
  595.  
  596.       putchar('\n');
  597.    }
  598.  
  599.    while (fp2 != NULL)
  600.    {
  601.       if (printsingle && (printempty || nextc(fp2) != '\n'))
  602.          printf(strf, bstring);
  603.  
  604.       put_line(fp2);
  605.  
  606.       if (nextc(fp2) == EOF)
  607.          fp2 = NULL;
  608.  
  609.       putchar('\n');
  610.    }
  611. }                /* End of attachf.*/
  612.  
  613. /*----------------------------------------------------------------------*
  614.  * put_line -- Get a line, print a line.                *
  615.  *----------------------------------------------------------------------*/
  616. put_line(fp)
  617. FILE        *fp;
  618. {
  619.    register int    c;
  620.  
  621.    while((c = getc(fp)) != '\n' && c != EOF)
  622.       putchar(c);
  623.  
  624. }            /* End of put_line.*/
  625.  
  626. /*----------------------------------------------------------------------*
  627.  * nextc -- What is the next character?  I realize that there are    *
  628.  *    some routines in some stdio.h files that do this for you, but    *
  629.  *    this is not true of all of them.  Hence this function.    *
  630.  *----------------------------------------------------------------------*/
  631. nextc(fp)
  632. FILE        *fp;
  633. {
  634.    register int    c;
  635.  
  636.    c = getc(fp);
  637.    ungetc(c, fp);
  638.  
  639.    return(c);
  640. }                /* End of nextc.*/
  641.  
  642. /*----------------------------------------------------------------------*
  643.  * exit_error -- Print out the appropriate error message for the    *
  644.  *    appropriate error, then exit.                *
  645.  *----------------------------------------------------------------------*/
  646. exit_error(errcode, details)
  647. int        errcode;
  648. char        *details;
  649. {
  650.    extern char    *error_msg[];
  651.  
  652.    fprintf(stderr, error_msg[errcode], program_name, details);
  653.    exit (1);
  654. }                /* End of exit_error.*/
  655.  
  656.  
  657.  
  658.  
  659.  
  660.  
  661.